use std::ops::Deref;

use objc2::rc::Id;
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2_app_kit::NSApplication;
use objc2_foundation::{MainThreadMarker, NSNumber, NSObject, NSObjectProtocol};

#[derive(Clone)]
pub struct AppProxy(pub Id<AppProxyObj>);
impl Deref for AppProxy {
	type Target = Id<AppProxyObj>;
	fn deref(&self) -> &Self::Target {
		&self.0
	}
}
impl types::AppProxy for AppProxy {
	fn wake(&self, id: i64) {
		use objc2_foundation::NSObjectNSThreadPerformAdditions;
		unsafe {
			self.performSelectorOnMainThread_withObject_waitUntilDone(
				objc2::sel!(updateInMain:), //
				Some(&NSNumber::new_i64(id)),
				false,
			)
		};
	}
}

declare_class!(
	pub struct AppProxyObj;
	unsafe impl ClassType for AppProxyObj {
		type Super = NSObject;
		type Mutability = mutability::Immutable;
		const NAME: &'static str = "XLoopAppProxy";
	}
	impl DeclaredClass for AppProxyObj {}
	unsafe impl AppProxyObj {
		#[method(updateInMain:)]
		fn update_in_main(&self, val: &NSNumber){ self.update_in_main_(val) }
	}
);
unsafe impl Send for AppProxyObj {}
unsafe impl Sync for AppProxyObj {}
impl AppProxyObj {
	pub(crate) fn new() -> Id<Self> {
		unsafe { msg_send_id![AppProxyObj::alloc(), init] }
	}
	fn update_in_main_(&self, val: &NSNumber) {
		let mtm = MainThreadMarker::new().unwrap();
		let app = NSApplication::sharedApplication(mtm);
		let value = val.as_i64();
		for win in &app.windows() {
			if win.respondsToSelector(sel!(updateProxy:)) {
				let _: () = unsafe { msg_send![win, updateProxy:value] };
			}
		}
	}
}
